﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Isuka.Iteration;
using Isuka.Linq;
using Isuka.Text;
using Isuka.Spiritual.Auxiliaries;

namespace Isuka.Spiritual
{
    /// <summary>
    /// 文字列に特化したルール
    /// </summary>
    /// <typeparam name="A"></typeparam>
    public class CharRules<A>
        : Rules<char, A>
        where A : class
    {
        /// <summary>
        /// 指定された文字列のどれかに一致
        /// </summary>
        /// <param name="values"></param>
        /// <returns></returns>
        public static ValueConvertibleRule<string, char, A> Case(params string[] values)
        {
            Contract.Requires<ArgumentNullException>(values != null, "values");
            Contract.Ensures(Contract.Result<ValueConvertibleRule<string, char, A>>() != null);

            return Case(values.Select(x => Eq(x)).ToArray()).ToConvertible();
        }

        /// <summary>
        /// 改行に一致
        /// </summary>
        public static readonly RuleHolder<char, A> EndOfLine = new RuleHolder<char, A>((i, c, s) =>
        {
            if (i.Equals(c.Last))
            {
                return _True(i);
            }
            else
            {
                var ch = i.Current;
                return ch == '\n' || ch == '\r' || ch.IsLineSeparator() ? _True(i) : _False(i);
            }
        });

        /// <summary>
        /// 文字列と一致
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static ValueConvertibleRule<string, char, A> Eq(string value)
        {
            Contract.Ensures(Contract.Result<ValueConvertibleRule<string, char, A>>() != null);

            return new ValueConvertibleRule<string, char, A>(_Eq(value), x => x.Stringize());
        }

        /// <summary>
        /// 行頭に一致
        /// </summary>
        public static readonly RuleHolder<char, A> StartOfLine = new RuleHolder<char, A>((i, c, s) =>
        {
            Contract.Ensures(Contract.Result<RuleHolder<char, A>>() != null);

            if (i.Equals(c.First))
            {
                return _True(i);
            }
            else
            {
                var ch = i.Prior().Current;
                return ch == '\n' || ch == '\r' || ch.IsLineSeparator() ? _True(i) : _False(i);
            }
        });

        /// <summary>
        /// 数字に一致
        /// </summary>
        /// <param name="min"></param>
        /// <param name="max"></param>
        /// <returns></returns>
        public static RuleHolder<char, A> Digits(int min = 1, int max = -1)
        {
            Contract.Requires(0 <= min);
            Contract.Requires(max == -1 || min <= max);
            Contract.Ensures(Contract.Result<RuleHolder<char, A>>() != null);

            return While(char.IsDigit, min, max);
        }

        /// <summary>
        /// 整数に一致
        /// </summary>
        /// <param name="numberGroupSeparator"></param>
        /// <param name="min"></param>
        /// <param name="max"></param>
        /// <returns></returns>
        public static RuleHolder<char, A> IntegralDigits(string numberGroupSeparator = ",", int min = 1, int max = -1)
        {
            Contract.Requires<ArgumentNullException>(numberGroupSeparator != null, "numberGroupSeparator");
            Contract.Requires<ArgumentOutOfRangeException>(0 <= min);
            Contract.Requires<ArgumentOutOfRangeException>(max == -1 || min <= max);
            Contract.Ensures(Contract.Result<RuleHolder<char, A>>() != null);

            return new RuleHolder<char, A>((i, c, s) =>
            {
                var ok = false;
                var count = 0;
                while (!i.Equals(c.Last))
                {
                    var ch = i.Current;
                    if (char.IsDigit(ch))
                    {
                        i.MoveNext();
                        ok = true;
                        count++;
                    }
                    else if (ok)    //  区切り記号は数字の後
                    {
                        var r = _SeqEq(i.Copy(), c.Last, numberGroupSeparator);
                        if (r.Match)
                        {
                            ok = false;  //  区切り記号で終わった場合にエラーさせるため
                            i = r.Iterator;
                        }
                        else
                        {
                            break;
                        }
                    }
                    else
                    {
                        break;
                    }
                }
                ok &= min <= count;
                if (max != -1)
                {
                    ok &= count <= max;
                }
                return _Result(i, ok);
            });
        }

        public static RuleHolder<char, A> HexDigits(int min = 1, int max = 10)
        {
            Contract.Requires<ArgumentOutOfRangeException>(0 <= min);
            Contract.Requires<ArgumentOutOfRangeException>(max == -1 || min <= max);
            Contract.Ensures(Contract.Result<RuleHolder<char, A>>() != null);

            return While(x => ('0' <= x && x <= '9') || ('A' <= x && x <= 'F') || ('a' <= x && x <= 'f'), min, max);
        }


        public static readonly ValueConvertibleRule<int, char, A> Int32
            = Number(Auxiliaries.NumberParserInfo.Integer, (x, i) => int.Parse(x, i.Styles, i.FormatInfo));

        public static readonly ValueConvertibleRule<double, char, A> Double
            = Number(Auxiliaries.NumberParserInfo.Real, (x, i) => double.Parse(x, i.Styles, i.FormatInfo))
            ;


        /// <summary>
        /// 数字に一致
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="info"></param>
        /// <param name="conv"></param>
        /// <returns></returns>
        public static ValueConvertibleRule<T, char, A>
            Number<T>(Auxiliaries.NumberParserInfo info
                    , Func<string, Auxiliaries.NumberParserInfo, T> conv)
        {
            Contract.Requires<ArgumentNullException>(info != null, "info");
            Contract.Ensures(Contract.Result<ValueConvertibleRule<T, char, A>>() != null);

            var rule = info.MakeNumberParser<A>();
            return new ValueConvertibleRule<T, char, A>(rule, x => conv(x.Stringize(), info));
        }


        public static readonly SkipFunc SkipWhitespace = SkipIf(x => char.IsWhiteSpace(x));

    }
}
